package org.vectrics.common.tree;



import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Category;
import org.vectrics.SystemException;


public abstract class TreeNodeImpl implements TreeNode, java.lang.Comparable {
    private static Category log = Category.getInstance(TreeNodeImpl.class);
    private Tree tree = null;
    private ArrayList children = null;
    private TreeNodeImpl parent = null;
    private String id;


    protected abstract TreeNode create();

    protected TreeNodeImpl() {
    }

    protected TreeNodeImpl(Tree tree) {
        this.tree = tree;
    }
    
    public TreeNode findNode(String id) {
    	TreeNode node = getTree().find(id);
    	return(node);
    }

/*    
    public void setTree(Tree tree) throws DuplicateNodeException {
    	log.debug("setTree() - method to set the tree containing this node");
        if (tree == null) {
            log.error("setTree() called with NULL tree.");
        }
        
        this.tree = tree;
        TreeNode existingNode = tree.find(this.getId()); 
        if (existingNode == null) {
        }
        else {
        	throw new DuplicateNodeException("TreeNode is already element within the tree: node id = " + this.getId());
        }
        
        tree.getMasterList().add(this);
        
        // Change any children to same tree.
        if ((children != null) && (children.size() > 0)) {
            Iterator iter = children.iterator();
            while (iter.hasNext()) {
                TreeNodeImpl child = (TreeNodeImpl)iter.next();
                child.setTree(tree);
            }
        }
    }
*/
    
    
    public void remove(TreeNode node, boolean recurse) {
        getChildren().remove(node);
        if (recurse) {
            Iterator iter = getChildren().iterator();
            while (iter.hasNext()) {
                TreeNodeImpl child = (TreeNodeImpl)iter.next();
                child.remove(node, recurse);
            }
        }
    }
    
    
    public Tree getTree() {
        if (tree == null) {
            if (parent == null) {
                log.debug("getTree() - tree was null, created new tree.");
                tree = new TreeImpl();
               	tree.setRoot(this);
            }
            else
                log.warn("getTree() called, but tree was null.");
        }
        return(tree);
    }
    
    public int compareTo(Object node)
    {
        if (hashCode() < node.hashCode())
            return(-1);
        else if (hashCode() > node.hashCode())
            return(1);
        else
            return(0);

    };

    
    public TreeNode addChild() {
        TreeNode child = create();
        log.debug("addChild() - class = " + child.getClass().getName());
        child.setParent(this);
        Collection childrenSet = getChildren();
        childrenSet.add(child);
        
        return(child);
    };

    
    public TreeNode addChild(TreeNode child)  {
        log.debug("addChild() called.");
        if (child.getId().equals(this.getId())) {
        	throw new SystemException("Child added to self error");
        }
        
        if (child == null)
            log.error("addChild() - cannont add null child to node.");
        else {
            log.debug("addChild() - getting children.");
            Collection childrenSet = getChildren();
            log.debug("addChild() - adding child to children.");
            child.setParent(this);
            childrenSet.add(child);
        }
        return(child);
    };

    public void setParent(TreeNode parent) {
        log.debug("setParent() Called");
        this.parent = (TreeNodeImpl)parent;
        //this.setTree(parent.getTree());
        this.tree = ((TreeNodeImpl)parent).getTree();
        this.tree.getMasterList().add(this);
    };


    public TreeNode getParent()
    {
        return(parent);
    };

    public final List getChildren()
    {
        if (children == null) {
            log.debug("getChildren() - creating children container.");
            children = new ArrayList();
        }
        return(children);
    };

    public int getDepth() {
        int depth = 0;
        TreeNode curParent = getParent();
        while (curParent != null) {
            depth++;
            curParent = curParent.getParent();
        };
        return(depth);
    };


    public TreeNode getNextSibling()
    {
        TreeNodeImpl next = null;
        if (getParent() != null) {
            Iterator iter = getParent().getChildren().iterator();
            boolean done = false;
            while ((iter.hasNext()) && (done == false)) {
                TreeNodeImpl child = (TreeNodeImpl)iter.next();
                if (child == this)
                    done = true;
            }
            if (iter.hasNext())
                next = (TreeNodeImpl)iter.next();
        };
        return(next);
    };


    public Collection getBranchCollection() {
        ArrayList set = new ArrayList();
        Iterator iter = getChildren().iterator();
        set.add(this);
        while (iter.hasNext()) {
            TreeNodeImpl child = (TreeNodeImpl)iter.next();
            Collection childBranchSet = child.getBranchCollection();
            set.addAll(childBranchSet);
        };
        return(set);
    }


    public void setId(String id) {
        this.id = id;
    }
    
    public String getId() {
        return id;
    }
    
    public String getHierarchy() {
        return(getHierarchy(0));
    }
        

    public String getHierarchy(int indent) {
        StringBuffer str = new StringBuffer();
        str.append("\r\n");
        Iterator iter = getChildren().iterator();
        while (iter.hasNext()) {
            
            TreeNodeImpl node = (TreeNodeImpl)iter.next();
            
            String indentStr = "";
            for (int i = 0; i < indent; i++) {
                indentStr += "- ";
            }
            str.append(indentStr);
            str.append(node.getId() + "\r\n");
            str.append(node.getHierarchy(indent + 1));
        }
        return(str.toString());
    }
    

    public int getLevel() {
    	int level = -1;
    	if (getParent() != null) {
    		level = getParent().getLevel() + 1;
    	}
    	else {
    		level = 0;
    	}
    	return(level);
    }
    
    
    public List getChildrenAtLevel(int relativeLevel) {
    	
    	int actualLevel = getLevel() + relativeLevel + 1;
    	
    	
    	List levelSkillList = new ArrayList();
    	if (this.getLevel() == actualLevel) {
    		levelSkillList.add(this);
    	}
    	else {
    		Iterator iter = this.getBranchCollection().iterator(); //this.getTree().getMasterList().iterator();
    		while (iter.hasNext()) {
    			TreeNode skill = (TreeNode)iter.next();
    			if (skill.getLevel() == actualLevel) {
    				levelSkillList.add(skill);
    			}
    		}
    		
    	}
    	
    	if (log.isDebugEnabled()) {
    		log.debug("getChildrenAtLevel() called - level = " + relativeLevel);
    		log.debug("getChildrenAtLevel() called - this = " + getId());
    		Iterator iter = levelSkillList.iterator();
    		while (iter.hasNext()) {
    			log.debug("getChildrenAtLevel() called - added child = " + ((TreeNode)iter.next()).getId());
    			
    		}
    	}
    	
    	return(levelSkillList);
    }
    
    
}